[Back]

MacOS X Server Release Notes Copyright © 1999 by Apple Computer, Inc. All Rights Reserved.

 

MacOS X Server Developer Release Notes:
ActiveX Support

1. ActiveX: important concepts

This chapter is intended for people who have little or no knowledge about COM, Automation and ActiveX objects. Please, proceed to the next chapter which details the specific implementation if you are already famililar with ActiveX development.

Note: ActiveX support in the Yellow frameworks is only available on Windows operating systems. This release note is provided on both Mach and Windows in order to have complete information on available on both platforms.

The COM architecture

The Component Object Model (COM) is the foundation of ActiveX. This technology permits to define, create and manage dynamic objects. COM also allows objects to interact across process and machine boundaries as easily as objects within a single process interact. COM objects are manipulated through interfaces which are group of functions prototypes that the object exposes to the world, these groups of function prototypes are equivalent to pure virtual base classes in C++ programming. Simple objects may support only a single interface. More complicated objects, such as embeddable objects, typically support several interfaces. Clients have access to a COM object only through a pointer to one of its interfaces, which, in turn, allows the client to call any of the methods that make up that interface. These methods determine how a client can use the object's data. There is one restriction that has to be taken into consideration: once defined, an interface cannot change. An object may only provide more functionality by adding new interfaces.

In a sense, COM interfaces are similar to Java interfaces, however a COM interface is not identified by a class name but by an interface identifier (IID) which is represented as a globally unique identifier (GUID) that precisely identifies an interface for an object. The same interface in another object will have the same IID. A GUID is a 16 bytes array that is guaranteed to be unique worldwide, it is usually expressed as a series of 32 hex digits in the so-called "portable format", such as {00020420-0000-0000-C000-000000000046} (by the way, this GUID represents the IID of the IDispatch interface which we are going to talk about later in this document). COM enforces that every object implements at least the IUnknown interface. This interface provides functions to control the life time of the object, through reference counting, and also functions to access the other interfaces the object supports. Moreover, all other interfaces must inherits from the IUnknown interface.

Clients locate objects available on a system by using their class identifier (CLSID). A CLSID is also a GUID and on Microsoft Windows, all ActiveX objects have their CLSID stored in the registry file under /HKEY_CLASSES_ROOT/CLSID (the registry can be read using RegEdit which is located inside the Windows folder). For example, the CLSID {EAB22AC3-30C1-11CF-A7EB-0000C05BAE0B} identifies the ActiveX control for Internet explorer. Fortunately, Microsoft has defined a different, less cryptic way to identify an ActiveX object. Under the object CLSID key in the Windows registry, there is a sub-key called ProgID. This key refers to the programatic identifier and contains an ASCII string which gives a more friendly name to the ActiveX object and usually, the ProgID is expressed as follow "applicationName.className.version". for example, the ProgID key for the Internet Explorer control (under HKEY_CLASSES_ROOT\CLSID\{EAB22AC3-30C1-11CF-A7EB-0000C05BAE0B}\ProgID) is defined as "Shell.Explorer.1". COM will also accept a ProgID as a mean to identify an object. However, unlike CLSIDs, ProgIDs are not guaranteed to be unique across the registry (or even to be defined for every object), the safest way to locate an object is to use its CLSID.

Automation

To use an object, you need to know what interfaces and functions it supports. Normally, an object's interfaces are detailed in the header files which means you know the object you are dealing with only at compile time. At run-time, it is impossible to use an object directly without any previous knowledge of its interfaces. To remedy this problem, COM introduces a mechanism called Automation, which lets a Client use an object methods and properties without any prior knowledge about it. Objects that can be used through Automation must support a specific interface called IDispatch. Those objects are usually referred to Automation Servers or Automated Objects. This interface provides functions which lists all the public methods and properties the object support in its dispatch interface (called a dispinterface), functions to get a dispatch identifier (DISPID) from a method or property name (a DISPID fills the same role as a selector) and a function to send a message to the object using a DISPID and arguments. The IDispatch interface is the door through which a Client can access an ActiveX object functionalities, this also means that when the Client queries for the IDispatch the ActiveX object has to initialize completely, which could take a long time for big applications such as Internet Explorer. This could be costly if the Client only wants to list the methods the object supports. This is why, an ActiveX Object is very often accompanied by a Type Library.

Type Libraries are the key part of Automation, as they describe the interface(s) exposed by the object(s) in an application. The actual mechanisms for accessing those interfaces, such as IDispatch, are almost secondary in importance. Type Libraries are also registered on Windows, they are all located under the /HKEY_CLASSES_ROOT/TypeLib key and a GUID is allocated for each one of them. Each registered ActiveX object may contain a reference to its Type Library in the registry under the TypeLib key. For example the Type Library for Internet Explorer is HKEY_CLASSES_ROOT\CLSID\{EAB22AC3-30C1-11CF-A7EB-0000C05BAE0B}\TypeLib = {EAB22AC0-30C1-11CF-A7EB-0000C05BAE0B}. Type Libraries have a number of interfaces implemented to support them and their Clients. In particular, ITypeLib and ITypeInfo are needed to query type libraries, and ICreateTypeLib and ICreateTypeInfo are used to create them. Those libraries are generally generated using the MIDL compiler which takes files written in IDL or ODL (interface/object description language). For example, Visual Basic read the Type Libraries for each object it loads, this allows a programmer to write code which uses the objects without requiring any header file, and Visual Basic can perform method and argument type verification at run-time. Type Libraries lists the methods and properties an object supports, it also describes the type for each argument a method accepts.

A method argument type or a property type can only be one of those types: Empty, Boolean, Unsigned Character,  Signed Short, Signed Long, Float, Double, Date, Currency, Unicode Character String, Automated Object, COM Object, Decimal value or Array. There is also an extra type called Variant which indicates that the argument or property will accept any value of the former types. A method argument can also take a value sent by reference.  Furthermore, a method may contain one or more optional arguments, which means that method does not require a value for the argument. Also, arguments or properties are weakly typed, they will take a value of a different type than expected which means that the method or property will automatically coerce the value into the desired type (if the two types are not coercable, the method will probably fail).

2. Writing the Objective C protocols for an ActiveX Control

At the time of this writing there is no automatic way to generate an Objective C protocol for a dispinterface, The protocol must be manually created using whatever information is available about the ActiveX object, if the IDL for the object is available then this can be used to write the protocol, otherwise some tools, such as OleView, can be used to read the TypeLib accompanying the object. (OleView is available free from Microsoft, http://www.microsoft.com/oledev/olecom/oleview.htm). OleView will generate an IDL file from a TypeLib.

Interfaces to use

An IDL contains detailed information about the interfaces it defines, but NSDispatchProxy and NSActiveXView event sink only needs one interface each. NSDispatchProxy will need the interface for Methods and properties, NSActiveXView event sink must implement the Event methods. Those interfaces are declared in class for the ActiveX control.

For example, the IDL file for the ActiveX control for Internet Explorer contains the following lines which defines the ActiveX control class:

   [
      uuid(EAB22AC3-30C1-11CF-A7EB-0000C05BAE0B),
      helpstring("WebBrowser Control"),
      control
    ]
    coclass WebBrowser {
        [default] interface IWebBrowser;
        [default, source] dispinterface DWebBrowserEvents;
    };

The methods and properties supported by the ActiveX control are implemented inside the IWebBrowser interface as indicated by the '[default] interface' prefix. The events that ActiveX control sends to the container are defined in DWebBrowserEvents dispinterface (indicated by '[default, source] dispinterface' prefix.

The IWebBrowser interface is listed in the IDL file as follow (make sure that the interface inherits from IDispatch, this is required for use with NSDispatchProxy):

   interface IWebBrowser : IDispatch {
        [id(0x00000064), helpstring("Navigates to the previous item in the history list.")]
        HRESULT GoBack();
        [id(0x00000065), helpstring("Navigates to the next item in the history list.")]
        HRESULT GoForward();
        [id(0x00000066), helpstring("Go home/start page.")]
        HRESULT GoHome();
        [id(0x00000067), helpstring("Go Search Page.")]
        HRESULT GoSearch();
        [id(0x00000068), helpstring("Navigates to a URL or file.")]
        HRESULT Navigate(
                        [in] BSTR URL,
                        [in, optional] VARIANT* Flags,
                        [in, optional] VARIANT* TargetFrameName,
                        [in, optional] VARIANT* PostData,
                        [in, optional] VARIANT* Headers);
        [id(0xfffffdda), helpstring("Refresh the currently viewed page.")]
        HRESULT Refresh();
        [id(0x00000069), helpstring("Refresh the currently viewed page.")]
        HRESULT Refresh2([in, optional] VARIANT* Level);
        [id(0x0000006a), helpstring("Stops opening a file.")]
        HRESULT Stop();
        [id(0x000000c8), propget, helpstring("Returns the application automation object if accessible, this automation object otherwise..")]
        HRESULT Application([out, retval] IDispatch** ppDisp);
        [id(0x000000c9), propget, helpstring("Returns the automation object of the container/parent if one exists or this automation object.")]
        HRESULT Parent([out, retval] IDispatch** ppDisp);
        [id(0x000000ca), propget, helpstring("Returns the container/parent automation object, if any.")]
        HRESULT Container([out, retval] IDispatch** ppDisp);
        [id(0x000000cb), propget, helpstring("Returns the active Document automation object, if any.")]
        HRESULT Document([out, retval] IDispatch** ppDisp);
        [id(0x000000cc), propget, helpstring("Returns True if this is the top level object.")]
        HRESULT TopLevelContainer([out, retval] VARIANT_BOOL* pBool);
        [id(0x000000cd), propget, helpstring("Returns the type of the contained document object.")]
        HRESULT Type([out, retval] BSTR* Type);
        [id(0x000000ce), propget, helpstring("The horizontal position (pixels) of the frame window relative to the screen/container.")]
        HRESULT Left([out, retval] long* pl);
        [id(0x000000ce), propput, helpstring("The horizontal position (pixels) of the frame window relative to the screen/container.")]
        HRESULT Left([in] long pl);
        [id(0x000000cf), propget, helpstring("The vertical position (pixels) of the frame window relative to the screen/container.")]
        HRESULT Top([out, retval] long* pl);
        [id(0x000000cf), propput, helpstring("The vertical position (pixels) of the frame window relative to the screen/container.")]
        HRESULT Top([in] long pl);
        [id(0x000000d0), propget, helpstring("The horizontal dimension (pixels) of the frame window/object.")]
        HRESULT Width([out, retval] long* pl);
        [id(0x000000d0), propput, helpstring("The horizontal dimension (pixels) of the frame window/object.")]
        HRESULT Width([in] long pl);
        [id(0x000000d1), propget, helpstring("The vertical dimension (pixels) of the frame window/object.")]
        HRESULT Height([out, retval] long* pl);
        [id(0x000000d1), propput, helpstring("The vertical dimension (pixels) of the frame window/object.")]
        HRESULT Height([in] long pl);
        [id(0x000000d2), propget, helpstring("Gets the short (UI-friendly) name of the URL/file currently viewed.")]
        HRESULT LocationName([out, retval] BSTR* LocationName);
        [id(0x000000d3), propget, helpstring("Gets the full URL/path currently viewed.")]
        HRESULT LocationURL([out, retval] BSTR* LocationURL);
        [id(0x000000d4), propget, helpstring("Query to see if something is still in progress.")]
        HRESULT Busy([out, retval] VARIANT_BOOL* pBool);
    };

There is no requirement to convert every single method and property into the Protocol, the translation process is fairly straight forward as shown by the examples below:  

A method defined as follow:

        [id(0x00000068), helpstring("Navigates to a URL or file.")]
        HRESULT Navigate(
                        [in] BSTR URL,
                        [in, optional] VARIANT* Flags,
                        [in, optional] VARIANT* TargetFrameName,
                        [in, optional] VARIANT* PostData,
                        [in, optional] VARIANT* Headers);

will translate in Objective C as:

    // Navigates to the previous item in the history list.
    - (void) navigate:(NSString *)url :(id)flags :(id)targetFrameName :(id)postData :(id)headers;

A read-only property (a read property is indicated by the propget token in the method description) defined as follow :

        [id(0x000000c8), propget, helpstring("Returns the application automation object if accessible, this automation object otherwise..")]
        HRESULT Application([out, retval] IDispatch** ppDisp);

will translates in Objective C as:

    // Returns the application automation object if accessible, this automation object otherwise...
    - (NSDispatchProxy *) application;

A read-write property (a read/write property is indicated by the propget, propput tokens in the method description) defined as follow :

        [id(0x000000d1), propget, helpstring("The vertical dimension (pixels) of the frame window/object.")]
        HRESULT Height([out, retval] long* pl);
        [id(0x000000d1), propput, helpstring("The vertical dimension (pixels) of the frame window/object.")]
        HRESULT Height([in] long pl);

will translates in Objective C as:

    // The vertical dimension (pixels) of the frame window/object.
    - (long) height;
    - (void) setHeight:(long)pl;

Since Objective-C does not distinguish between properties and methods, it is possible that some methods and properties name clashes (do not confuse with selector names, in this case a method or method name does not take any parameter into account). If this happens, the problematic property names can be renamed using the prefix 'get' for reading and replacing the prefix 'set ' by 'put'  for writing.

For example the property 'Height' is defined as follow:

    - (long) height;
    - (void) setHeight:(long)pl;

may be redefined as:

    - (long) getHeight;
    - (void) putHeight:(long)pl;

The marshalling process in the ActiveX framework is not case sensitive for methods and property names, you may give your Protocol methods names a more Objective-C like flavour .

Marshalling the arguments

All the basic C types are supported directly, as well as those Objective-C object classes:

ActiveX Automation does not natively supports those basic types: char, unsigned short, unsigned int, unsigned long. However, the marshalling process will accept values for those types under certain conditions: a char value is considered to contain a boolean, to support the type BOOL. Other types cast into a supported type depending on their value. For example, an unsigned long value downcast into a long if the value can fit into it, otherwise the value upcast into a double.

The returned value of a method can only be of type:  BOOL, unsigned char, short, long, float , double, NSString, NSNumber, NSDate, NSDispatchProxy, NSSafeArray. For basic c-types, the marshalling process coerces the actual value into the desired type. For objects, the value returned is the object that matches closely the actual returned value.

For arguments sent by reference, the supported types are more restricted: BOOL *, unsigned char *, short *, long *, float *, double *, NSString **, NSNumber ** (only for returned values in argument), NSDate **, NSDispatchProxy **, NSSafeArray **. If only a returned a value is expected for an argument, then an object pointer may be used which initially contains nil, otherwise the special Objective-C keywords in and out are also recognized. Other types may work only if the in keyword is explicitly used, the marshalling process dereferences those values and pass them to the ActiveX object. Bear in mind that the returned value for an object argument could be an object of a class completly different than the ingoing object since there is no restriction imposed on objects. The untyped pointer type (void *) is defined to represent a generic COM object, it can be used as an argument, setting a property or as a returned value. (void **) is then a pointer to a COM object.

The Automation types Currency and Decimal are not directly supported, however the marshalling process uses an NSNumber to contain their values (with a possible lost of precision) whenever they are encountered.

Any argument of type VARIANT * declared in the Protocol takes a value that is not marshalled and is sent directly to the object unmodified, the argument is considered to be in-out unless specified otherwise.

Optional arguments may not have a value, the only way to express this in Objective C is to set the type of the argument to any pointer type (which include the object type) and send nil as a value.

Unsupported features

This ActiveX framework does not support all ActiveX features. All the required features are in place, but some optional, nice to have, featues are not yet implemented or are only partially supported.

NSDispatchProxy

NSDispatchProxy does not use Type Libraries, it completly relies on IDispatch to locate methods and properties. This can cause problems when NSDispatchProxy is trying to distinguish between methods and properties. There is a known problem with Automated object that are only a bridge to java classes (eg. java beans). Java methods starting with get and set are automatically mapped into properties by the bridge, this may confuse NSDispatchProxy because the java method exists as a two properties with different names in the ActiveX world. There is a workaround to this problem which is illustrated by the followingexample:

A java class implements a method as follow:

String getAuthorName(String Book)
.

The Java to ActiveX bridage will probably defined these two entries internally as:

[id(0x010000), progget]
BSTR AuthorName(BSTR Book);
[id(0x010001), propget]
BSTR getAuthorName(BSTR Book);

The published Type Lib usually lists only the first entry.
Converted to a protocol, this will show as:

- (NSString *)authorName:(NSString *)book;
or
- (NSString *)getAuthorName:(NSString *)book;

calling either of those methods may fail because NSDispatchProxy fail to recognize a property and assumes a method. If this the case then rename the protocol entry as:

- (NSString *)getGetAuthorName:(NSString *)book;

this should fix the problem.

NSActiveXView

NSActiveXView does not support persistent storage for a control. If the control does not explicitly provide a save method, then its content will be lost every time the control is discarded.

NSActiveXView relies on the control to do its own clipping when neccessary. However, some controls do not support clipping at all (for example, Internet Explorer), it is then not advisable to use an NSActiveXView inside a NSScrollView or similar views. the next version of NSActiveXView will manage clipping without relying on the control to do it.

Some controls insert menus when selected. However, control menus will not be merged into the Application menus, they will be displayed on their own. Application menus are restored when the control is deselected.

NSActiveXView does not provide any ambient properties to the embedded control. Some controls reacts badly to that.

Class Documentation

NSDispatchProxy

Inherits From:
NSProxy

Declared In:
ActiveX/NSDispatchProxy.h

Class Description

NSDispatchProxy is a concrete subclass of NSProxy that defines proxies for ActiveX Automation objects. When an NSDispatchProxy receives a message, in most cases it forwards the message through DCOM (Distributed Component Object Model) to the real ActiveX Automation object, supplying the return value to the sender of the message if one is forthcoming, and propagating any exception back to the invoker of the method that raised it.

NSDispatchProxy adds seven instance methods to those defined by NSProxy. -initWithDispatch:,  -initWithClassID:remoteServer: and -initWithProgID:remoteServer: method creates a proxy for the local or remote ActiveX Automation object and -setProtocolForProxy: and -setProtocolForProxy:localeContext: method establishes the set of methods that the real ActiveX Automation object is known to respond to, as in:

    NSDispatchProxy <treeView> *treeview;
    NSDispatchProxy <nodes> *nodes;

    treeview = [NSDispatchProxy proxyWithClassId:@"{0713E8A2-850A-101B-AFC0-4210102A8DA7}" remoteServer:nil];
    [treeview setProtocolForProxy: @protocol(treeView)];

    nodes = [treeview nodes];
    [nodes setProtocolForProxy: @protocol(nodes)];
    
    [nodes add: nil :nil :nil :@"1" :nil :nil];
    [nodes add: nil :nil :nil :@"2" :nil :nil];
    [nodes add: nil :nil :nil :@"3" :nil :nil];

-objectEnumerator returns an enumerator object if a proxy represents a collection object and -dispatchForProxy returns the IDispatch interface of the ActiveX Automation object.

Note: The UNC and DNS below represents Universal Naming Convention and Domain Name Service.

Method Types

Creating a proxy

    + proxyWithDispatch:
    - initWithDispatch:
    + proxyWithClassID: remoteServer:
    - initWithClassID: remoteServer:
    + proxyWithProgID: remoteServer:
    - initWithProgID: remoteServer:

Getting a proxy's IDispatch

    - dispatchForProxy

Setting a proxy's Protocol

    - setProtocolForProxy:
    - setProtocolForProxy:localeContext:

Accessing an object's collection

    - objectEnumerator

Class Methods

proxyWithClassID: remoteServer:

+ (NSDispatchProxy *)proxyWithClassId:(NSString *)classIdString remoteServer:(NSString *)remoteServerName;

Returns a proxy for classIdString, which is the CLSID (class identifier) or ProgID (programatic identifier) for an ActiveX Automation object on remoteServerName. The CLSID is in the form "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}", where x is a valid hexadecimal number. The remoteServerName is in the UNC form ("\\server" or "server") or the DNS form ("server.com", "www.server.com" or "xxx.xxx.xxx.xxx", where x is a valid decimal number). If remoteServerName is nil and RemoteServerName registry value for the given object is not specified, an ActiveX Automation object is created on a local server, as in:

    NSDispatchProxy <treeView> *treeview;

    treeview = [NSDispatchProxy proxyWithClassID:@"{0713E8A2-850A-101B-AFC0-4210102A8DA7}" remoteServer:nil];
    [treeview setProtocolForProxy: @protocol(treeView)];

proxyWithDispatch:

+ (NSDispatchProxy *)proxyWithDispatch:(IDispatch *)dispatch;

Returns a proxy for dispatch, which is the IDispatch interface of an ActiveX Automation object that has already been created.

proxyWithProgID: remoteServer:

+ (NSDispatchProxy *)proxyWithProgID:(NSString *)progIdString remoteServer:(NSString *)remoteServerName;

Returns a proxy for progIdString, which is the ProgID (programatic identifier) for an ActiveX Automation object on remoteServerName. The ProgID is a character string usually in the form "appName.className.version". The remoteServerName is in the UNC form ("\\server" or "server") or the DNS form ("server.com", "www.server.com" or "xxx.xxx.xxx.xxx", where x is a valid decimal number). If remoteServerName is nil and RemoteServerName registry value for the given object is not specified, an ActiveX Automation object is created on a local server, as in:

    NSDispatchProxy <IWebBrowser> *webBrowser;

    treeview = [NSDispatchProxy proxyWithProgID:@"Shell.Explorer.1" remoteServer:nil];
    [treeview setProtocolForProxy: @protocol(IWebBrowser)];

Instance Methods

dispatchForProxy

- (IDispatch *)dispatchForProxy;

Returns the IDispatch interface used by the receiver. The interface has to be released when it is no longer required.

initWithClassID: remoteServer:

- (NSDispatchProxy *)initWithClassId:(NSString *)classIdString remoteServer:(NSString *)remoteServerName;

Initializes a newly allocated NSDispatchProxy as a proxy for classIdString, which is the CLSID (class identifier) for an ActiveX Automation object on remoteServerName. The CLSID is in the form "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}", where x is a valid hexadecimal number. The remoteServerName is in the UNC form ("\\server" or "server") or the DNS form ("server.com", "www.server.com" or "xxx.xxx.xxx.xxx", where x is a valid decimal number). If remoteServerName is nil and RemoteServerName registry value for the given object is not specified, an ActiveX Automation object is created on a local server.

Returns self.

initWithDispatch:

- (NSDispatchProxy *)initWithDispatch:(IDispatch *)dispatch;

Initializes a newly allocated NSDispatchProxy as a proxy for dispatch, which is the IDispatch interface of an ActiveX Automation object that has already been created.

Returns self.

initWithProgID: remoteServer:

- (NSDispatchProxy *)initWithClassID:(NSString *)progIdString remoteServer:(NSString *)remoteServerName;

Initializes a newly allocated NSDispatchProxy as a proxy for progIdString, which is the ProgID (programatic identifier) for an ActiveX Automation object on remoteServerName. The ProgID is a character string usually in the form "appName.className.version". The remoteServerName is in the UNC form ("\\server" or "server") or the DNS form ("server.com", "www.server.com" or "xxx.xxx.xxx.xxx", where x is a valid decimal number). If remoteServerName is nil and RemoteServerName registry value for the given object is not specified, an ActiveX Automation object is created on a local server.

Returns self.

objectEnumerator

- (NSEnumerator *)objectEnumerator;

Returns an enumerator object that lets you access each item in a collection object, as in:

    NSDispatchProxy <nodes> *nodes;
    NSDispatchProxy <node> *node;
    NSEnumerator *enumerator;

    enumerator = [nodes objectEnumerator];

    while (node = [enumerator nextObject]) {
        [node setProtocolForProxy: @protocol(node)];
        /* code to act on each object as it is returned */    
    }

setProtocolForProxy:

- (void)setProtocolForProxy:(Protocol *)protocol;

Sets the methods known to be handled by the receiver to those in protocol.

setProtocolForProxy:localeContext:

- (void)setProtocolForProxy:(Protocol *)protocol localeContext:(LCID)locale;

Sets the methods for this specific language known known to be handled by the receiver to those in protocol. By default, NSDispatchProxy assumes LOCALE_SYSTEM_DEFAULT for its locale context. Since Objective-C does not support methods names written in anything other than ASCII characters, it might be sensible to set the locale context to a language which uses ASCII characters for naming methods and properties.

NSActiveXView

Inherits From:
NSView

Declared In:
ActiveX/NSActiveXView.h

Class Description

NSActiveXView is a concrete subclass of NSView that provides a container for one ActiveX control. The ActiveX control must at least support in-place activation to be of any use inside this container. The control will be activated in place when the view needs to display, if the view is removed from its superview, the control is then deactivated. The view can become first responder if the control supports UI activation, and when the control is UI activated, the view will become first responder, if this is possible, otherwise the control will be UI deactivated. The control adopts the size and position of the view inside its superview. If the view resizes or moves, the control metrics will change accordingly. Some controls can resize or move by themselves, usually they dispay grips or handles at each corner of the view. If they do so, the view's delegate will be consulted to confirm the change. If the change is approved, both control and view will resize or move accordingly inside the NSWindow.  

Method Types

Creating an ActiveX view

    + viewWithFrame:
    -  initWithFrame:

    + viewWithFrame:controlWithProxy;
    - initWithFrame:controlWithProxy:

    + viewWithFrame:controlWithCOMObject:
    - initWithFrame:controlWithCOMObject:

Embedding and retreiving a control inside an ActiveX view

   - setControlWithProxy:
   - controlProxy
   - setControlWithCOMObject:
   - controlCOMObject

Setting an object which receives the control's events

   - setEventSink:(id)anObject;
   - eventSink;

Setting and retreiving the delegate for the ActiveX view

- delegate;
- setDelegate:

Class Methods

viewWithFrame:

+ (NSActiveXView *)viewWithFrame:(NSRect)aFrame;

Returns an empty ActiveX view or nil if the view failed to initialize.

viewWithFrame:controlWithCOMObject:

+ (NSActiveXView *)viewWithFrame:(NSRect)aFrame controlWithCOMObject:(IUnknown *)aCOMObject;

Returns an ActiveX view which embeds the ActiveX control pointed to by the aCOMObject interface or nil if the view failed to initialize or the ActiveX control could not be embedded. The ActiveX object is retained until the view gets deallocated or until it is explicitly removed from the view.

viewWithFrame:controlWithProxy:

+ (NSActiveXView *)viewWithFrame:(NSRect)aFrame controlWithProxy:(NSDispatchProxy *)aDispatchProxy;

Returns an ActiveX view which embeds the ActiveX control whose proxy is aDispatchProxy or nil if the view failed to initialize or the ActiveX control could not be embedded.

Note: aDispatchProxy is not retained. However the ActiveX object it refers to is.

Instance Methods

controlCOMObject

- (IUnknown *)controlCOMObject;

Returns a pointer to the IUnknown interface for the object currently embedded in the view or nil if there is no control embedded. The interface must be explictly released when it is no longer required.

controlProxy

- (NSDispatchProxy *)controlProxy;

Returns a proxy to the object currently embedded in the view or nil if there is no control embedded or the control does not support the IDispatch interface.

delegate

- (id)delegate;

Returns the receiver's delegate.

eventSink

- (id)eventSink;

Returns the receiver which responds to the ActiveX control events or nil if events are ignored.

initWithFrame:

- (NSActiveXView *)initWithFrame:(NSRect)aFrame;

Initialize a newly allocated ActiveX view or nil if the view failed to initialize.

initWithFrame:controlWithProxy:

- (NSActiveXView *)initWithFrame:(NSRect)aFrame controlWithProxy:(NSDispatchProxy *)aDispatchProxy;

Initialize a newly allocated ActiveX view and embeds the ActiveX control whose proxy is aDispatchProxy.

Returns self or nil if the view failed to initialize or the ActiveX control could not be embedded.

Note: aDispatchProxy is not retained. However the Automation object it refers to is.

initWithFrame:controlWithCOMObject:

- (NSActiveXView *)initWithFrame:(NSRect)aFrame controlWithCOMObject:(IUnknown *)aCOMObject;

Initialize a newly allocated ActiveX view and embeds the ActiveX control refered to by the pointer to its initialized aCOMObject interface.

Returns self or nil if the view failed to initialize or the ActiveX control could not be embedded.

setControlWithCOMObject:

- (BOOL)setControlWithCOMObject:(IUnknown *)aCOMObject;

Remove and release current ActiveX control from view and embedds the ActiveX control refered to by the pointer to its initialized aCOMObject interface. If aCOMObject is nil then the view becomes empty. The control will be activated in place when the view needs to display.

Returns YES if control has been successfully embedded, NO otherwise.

setControlWithProxy:

- (BOOL)setControlWithProxy:(NSDispatchProxy *)aDispatchProxy;

Remove and release current ActiveX control from view and embedds the ActiveX control whose proxy is aDispatchProxy. If aDispatchProxy is nil then the view becomes empty. The control will be activated in place whenever the view needs to display.

Returns YES if control has been successfully embedded, NO otherwise.

setDelegate:

- (void)setDelegate:(id)anObject;

Makes anObject the receiver's delegate. The messages that a delegate can expect to receive are listed at the end of this specification. The delegate doesn't need to implement all the methods.

setEventSink:

- (void)setEventSink:(id)anObject;

The ActiveXView sets anObject as the receiver of the events sent by the ActiveX control. if anObject is nil then these events are ignored. anObject does not have to respond to every event sent by the control. Some controls do not send events, in which case the event sink will not receive any events.

Note: anObject is not retained.

Delegate Methods

activeXView:controlWillChangeFrame:

- (NSRect)activeXView:(NSActiveXView)theView controlWillChangeFrame:(NSRect) proposedFrame

Sent directly by theView to the delegate whenever a control attempt to change its size and/or position inside a window (not the view it is embedded in). The Delegate should return proposedFrame if it agrees with the change. It can also returns [theView frame] to force the control to stay in its current position and size or any other frame rectangle as it wishes. If this method is not implemented by the delegate, the control is free to change its size and position.

NSSafeArray

Inherits From:
NSObject

Declared In:
ActiveX/NSSafeArray.h

Class Description

NSSafeArray wraps around a SAFEARRAY object which is used by Win32 APIs to handle a multidimentional mono-typed array. It is similar to a Visual Basic array and the main difference with a C array is that the lower bound is not restricted to 0 for any dimensions. This object should be primarily used for marshalling Objective-C objects into a safe array. Also, The objects written inside the safe array are not retained in any way since the array retains the marshalled value for the object.

Method Types

Creating a Safe Array

    + safeArrayWithType:dimensions:andBounds:
    - initWithType:dimensions:andBounds:

    + (id)safeArrayWithArray:
    - initWithArray:

    + (id)safeArrayWithData:;
    - initWithData:

    + (id)safeArrayWithArrayVariant:;
    - initWithArrayVariant:

    + (id)safeArrayWithArrayVariantNoCopy:
    - initWithArrayVariantNoCopy:takeOwnership:

Retreiving information about the Safe array.

    - dimensions;
    - physicalSafeArray;
    - variantType;
    - lowerBoundForDimension:
    - upperBoundForDimension:

Setting and retreiving elements from the Safe Array.

    - setObject:AtIndices:
    - objectAtIndices:

Locking and unlocking a Safe Array against destruction.

    - lockArray;
    - unlockArray;

Class Methods

safeArrayWithArray:

+ (NSSafeArray *)safeArrayWithArray:(NSArray *)array;

Returns a one dimension safe array (0 to [array length]-1) filled with each object from array marshalled into the type VT_VARIANT. The objects contained in the NSArray must be a class or subclass of one of the following classes: NSString, NSNumber, NSDate, NSDispatchProxy, NSSafeArray, NSData and NSArray.

safeArrayWithArrayVariant:

+ (NSSafeArray *)safeArrayWithArrayVariant:(const VARIANT *)variant;

Returns a safe array which contains a copy of array in variant.

safeArrayWithArrayVariantNoCopy:

+ (id)safeArrayWithArrayVariantNoCopy:(VARIANT *)variant;

Returns a NSSafeArray object which wraps around the safe array contained in variant, the safe array will be destroyed when NSSafeArray deallocates (the variant is returned initialized to empty) unless the variant is refering to an array sent by reference. In this case, NSSafeArray will not attempt to destroy the array on deallocation (the variant is returned untouched). All other initialization methods assumes that the safe array will be destroyed on deallocation.

safeArrayWithData:

+ (NSSafeArray *)safeArrayWithData:(NSData *)data;

Returns a one dimension safe array filled with the bytes from data. The array is initialized with the type VT_UI1 (unsigned char).

safeArrayWithType:dimensions:andBounds:

+ (NSSafeArray *)safeArrayWithType:(VARTYPE)type dimensions:(UINT)dimensions andBounds:(const SAFEARRAYBOUND *)bounds;

Returns an empty safe array which is created using similar arguments as the Win32 API 'SafeArrayCreate'. type is a Variant Type, which could be for instance VT_INT, VT_BSTR, VT_DATE... dimensions is the number of dimensions the array holds. bounds contains an array of bounds for each dimension.

Instance Methods

bytes

- (void *)bytes;

Returns the bytes contained in the SAFEARRAY object. The safe array has to be locked before this method can be used if  the array is not owned. This method will return NULL if the safe array is not locked. This method is the only method which requires the safe array to be locked since it is the only method which allow direct access to the SAFEARRAY data.

dimensions

- (unsigned int)dimensions;

Returns the number of dimensions.

getBytes:

- (void)getBytes:(void *)buffer;

Copies a SAFEARRAY object's contents into buffer.

getBytes:length:

- (void)getBytes:(void *)buffer length:(unsigned int)length

Copies up to length bytes from the start of the SAFEARRAY object's contents into buffer.

getBytes:range:

- (void)getBytes:(void *)buffer range:(NSRange)range

Copies the SAFEARRAY object's contents into buffer , from range that is within the bytes in the object. If range isn't within the receiver's range of bytes, an NSRangeException is raised.

initArrayWithArray:

+ (NSSafeArray *)initArrayWithArray:(NSArray *)array;

Initialize a newly allocated NSSafeArray object with a one dimension safe array (0 to [array length]-1) filled with each object from array marshalled into the type VT_VARIANT. The objects contained in the NSArray must be a class or subclass of one of the following classes: NSString, NSNumber, NSDate, NSDispatchProxy, NSSafeArray, NSData and NSArray.

initArrayWithArrayVariant:

+ (NSSafeArray *)initArrayWithArrayVariant:(const VARIANT *)variant;

Initialize a newly allocated NSSafeArray object with a safe array which contains a copy of array in variant.

initArrayWithArrayVariantNoCopy:takeOwnership:

+ (id)safeArrayWithArrayVariantNoCopy:(VARIANT *)variant takeOwnership:(BOOL)ownership

Initialize a newly allocated NSSafeArray object and  wrap it around the safe array contained in variant. The argument ownership specifies whether the NSSafeArray contains or wraps around the array in variant. If ownership is YES then the NSSafeArray object will destroy the safe array on deallocation, variant is returned initialize to empty. Otherwise it will not be deallocated and variant is returned untouched. The reason to refuse ownership of an array is usually in the sole purpose of marshalling Objective C objects in an unowned SAFEARRAY.

Note: if variant contains a safe array sent by reference, then the value in ownership is ignored, the NSSafeArray will not attempt to destroy the safe array when it deallocates.

initArrayWithData:

+ (NSSafeArray *)initArrayWithData:(NSData *)data;

Initialize a newly allocated NSSafeArray object with a one dimension safe array filled with the bytes from data. The array is initialized with the type VT_UI1 (unsigned char).

initArrayWithType:dimensions:andBounds:

+ (NSSafeArray *)initArrayWithType:(VARTYPE)type dimensions:(UINT)dimensions andBounds:(const SAFEARRAYBOUND *)bounds;

Initialize a newly allocated NSSafeArray object with an empty safe array which is created using similar arguments as the Win32 API 'SafeArrayCreate'. type is a Variant Type, which could be for instance VT_INT, VT_BSTR, VT_DATE... dimensions is the number of dimensions the array holds. bounds contains an array of bounds for each dimension.

isEqual:

- (BOOL)isEqual:(id)anObject;

Returns YES if the receiver and anObject are equal; otherwise returns NO . A YES return value indicates that the receiver and anObject are both instances of classes that inherit from NSSafeArray and that both contain the same data (as determined by the isEqualToSafeArray: method).

isEqualToSafeArray:

- (BOOL)isEqualToSafeArray:(NSSafeArray *)otherSafearray;

Compares the receiving data object to otherSafearray . If the contents of otherSafearray are equal to the contents of the receiver, this method returns YES . If not, it returns NO . Two NSSafeArray objects are equal if they hold the same number of values, and if the values at the same position in the objects are the same.

lockArray

- (void)lockArray;

Locks the SAFEARRAY object so that the array cannot be disposed of. You have to be careful when using this method because the object will not unlock the array when deallocating, this may result in a memory leak. Throw a NSGeneric exception if array could not be locked.

lowerBoundForDimension:

- (long)lowerBoundForDimension:(unsigned int)dimension;

Returns the lower bound for dimension, will raise an NSRangeException if dimension is out of range.

objectAtIndices:

- (id)objectAtIndices:(const long *)indices;

Returns an Objective-C object for the value stored at position indicated by indices. The argument indices is a pointer to a vector of indices for each dimension of the array. The right-most (least significant) dimension is indices[0]. The left-most dimension is stored at indices[dimensionsÜ 1]. An NSRangeException is thrown if one or more indices are out of range. May return nil if an unexpected error occured during unmarshallling.

physicalSafeArray

- (SAFEARRAY *)physicalSafeArray;

Returns the SAFEARRAY object. If the array is owned, the array is locked so that it cannot be disposed of using standard Win32 APIs.

setObject:atIndices:

- (void)setObject:(id)anObject atIndices:(const long *)indices;

Marshall anObject into a value that is stored at postion indicated by indices, the previous value is discarded. An NSRangeException is thrown if one or more indices are out of range. If necessary anObject is coerced into the array type, and a NSGenericException is thrown if object coercion failed. For example, if the array contains strings (VT_BSTR type) and anObject is an NSNumber object, anObject will be coerced into a string containing a decimal representation of your number whose formatting depends on your Windows preferences.

unlockArray

- (void)unlockArray;

Unlocks the SAFEARRAY object, lockArray and unlockArray calls should be balanced. NSSafeArray object automatically locks the SAFEARRAY object if it owns it and unlocks it before destroying it. In this case, there is no need to use locking methods. If the array if not own, it is advised to use locking methods to guarantee that the array will be present during the transaction. locking methods does not prevent other threads accessing/modifying your array, they just guarantee that the array will not be deallocated by someone else. Conventional locking mechanism should be used for thread safe operations. Throw a NSGeneric exception if array could not be unlocked.

upperBoundForDimension:

- (long)upperBoundForDimension:(unsigned int)dimension;

Returns the upper bound for dimension, will raise an NSRangeException if dimension is out of range.

variantType

- (VARTYPE) variantType;

Returns the Variant type of object the safe array contains. This type can be one of the following: VT_I2, VT_I4, VT_R4, VT_R8, VT_CY, VT_DATE, VT_BSTR, VT_DISPATCH, VT_ERROR, VT_BOOL, VT_VARIANT, VT_UNKNOWN and VT_UI1.